package pub.cleangao.memm.transport;

import lombok.SneakyThrows;
import pub.cleangao.memm.exception.RpcErrorMessage;
import pub.cleangao.memm.exception.RpcException;
import pub.cleangao.memm.registry.RpcServiceConfig;
import pub.cleangao.memm.transport.message.RpcRequest;
import pub.cleangao.memm.transport.message.RpcResponse;
import pub.cleangao.memm.transport.message.RpcResponseCode;
import pub.cleangao.memm.util.IDWorker;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


public record RpcClientProxy(RequestTransport rpcRequestTransport,
                             RpcServiceConfig rpcServiceConfig) implements InvocationHandler {


    /**
     * get the proxy object
     */
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Class<T> clazz) {
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]{clazz}, this);
    }

    /**
     * This method is actually called when you use a proxy object to call a method.
     * The proxy object is the object you get through the getProxy method.
     */
    @SneakyThrows
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        RpcRequest rpcRequest = new RpcRequest();
        rpcRequest.setMethodName(method.getName());
        rpcRequest.setParameters(args);
        rpcRequest.setInterfaceName(method.getDeclaringClass().getName());
        rpcRequest.setParamTypes(method.getParameterTypes());
        rpcRequest.setRequestId(IDWorker.nextIdChar());
        rpcRequest.setGroup(rpcServiceConfig.getGroup());
        rpcRequest.setVersion(rpcServiceConfig.getVersion());
        rpcRequest.setTimeout(rpcServiceConfig.getTimeout());
        RpcResponse<Object> rpcResponse = rpcRequestTransport.send(rpcRequest);
        this.check(rpcResponse, rpcRequest);
        return rpcResponse.getData();
    }

    private void check(RpcResponse<Object> rpcResponse, RpcRequest rpcRequest) {
        if (rpcResponse == null) {
            throw new RpcException(RpcErrorMessage.INVOCATION_FAILURE, rpcRequest.getInterfaceName());
        }
        if (!rpcRequest.getRequestId().equals(rpcResponse.getRequestId())) {
            throw new RpcException(RpcErrorMessage.REQUEST_NOT_MATCH_RESPONSE, rpcRequest.getInterfaceName());
        }
    }


}
